<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>文件上传</title>
  </head>
  <body>
    <h1>
      文件上传-可以拖拽
      <button><a href="/files" target="_self">文件列表</a></button>
    </h1>
    <input type="file" id="file" name="sampleFile" />
    <button onclick="uploadFile()">上传</button>

    <div id="progress"></div>
    <div>
      <span id="totalSize"></span>
      <span id="uploadedSize"></span>
    </div>

    <div>
      <span id="estimatedTime"></span>
      <span id="actualTime"></span>
    </div>

    <div id="mergeStatus"></div>
    <div
      id="dropZone"
      style="
        margin-top: 30px;
        width: 300px;
        height: 200px;
        border: 2px dashed #ccc;
        background-color: #fff;
        text-align: center;
        line-height: 200px;
        cursor: pointer;
      "
    >
      将文件拖拽至此区域上传
    </div>
  </body>
  <script>
    const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB 每一块
    const serverURL = "http://localhost:8001";
    async function uploadFile() {
      const fileInput = document.getElementById("file");
      const file = fileInput.files[0];

      if (!file) {
        alert("请先选择一个文件");
        return;
      }

      console.log("[file]", file);

      document.getElementById("mergeStatus").innerText = "";

      const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
      document.getElementById("totalSize").innerText =
        "总大小: " + formatBytes(file.size) + " / ";

      startTime = new Date().getTime(); // 记录开始上传的时间

      for (let i = 0; i < totalChunks; i++) {
        await uploadChunk(
          file,
          i * CHUNK_SIZE,
          (i + 1) * CHUNK_SIZE,
          file.name,
          totalChunks,
          i
        );
      }

      endTime = new Date().getTime(); // 记录结束上传的时间

      const actualTime = ((endTime - startTime) / 1000).toFixed(2); // 实际耗时，单位：秒
      document.getElementById("actualTime").innerText =
        "上传耗时: " + actualTime + "s";

      //   // 请求合并文件
      await mergeFile(file.name, totalChunks);

      // alert('上传完成');
    }

    function uploadChunk(
      file,
      start,
      end,
      fileName,
      totalChunks,
      currentChunk
    ) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        const chunk = file.slice(start, end);
        const formData = new FormData();

        const fileTempName = fileName + "_temp_" + (currentChunk + 1);

        formData.append("sampleFile", chunk, fileTempName);

        formData.append("currentChunk", currentChunk);
        formData.append("totalChunks", totalChunks);

        xhr.open("POST", serverURL + "/upload", true);

        xhr.upload.onprogress = function (e) {
          if (e.lengthComputable) {
            const percentComplete =
              ((e.loaded / e.total) * 100) / totalChunks +
              (100 * currentChunk) / totalChunks;

            document.getElementById("progress").innerText =
              "上传进度: " + percentComplete.toFixed(2) + "%";
            document.getElementById("uploadedSize").innerText =
              "已上传: " + formatBytes(start + e.loaded);
            // 更新已上传的字节数
            uploadedBytes = start + e.loaded;
            // 计算上传速度
            const currentTime = new Date().getTime();
            const elapsedTime = (currentTime - startTime) / 1000; // 经过的时间，单位：秒
            const uploadSpeed = uploadedBytes / elapsedTime; // 上传速度，单位：字节/秒
            // 估算预计耗时
            const remainingBytes = file.size - uploadedBytes; // 剩余待上传的字节数
            const estimatedTime = (remainingBytes / uploadSpeed).toFixed(2); // 预计耗时，单位：秒
            document.getElementById("estimatedTime").innerText =
              "剩余时间: " + estimatedTime + "s";
          }
        };

        xhr.onload = function () {
          if (xhr.status === 200) {
            resolve();
          } else {
            reject();
          }
        };

        xhr.onerror = reject;
        xhr.send(formData);
      });
    }

    function mergeFile(fileName, totalChunks) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("POST", serverURL + "/merge", true);
        xhr.setRequestHeader("Content-Type", "application/json");

        xhr.onload = function () {
          if (xhr.status === 200) {
            document.getElementById("mergeStatus").innerText = "合并完成";
            resolve();

            endTime = new Date().getTime(); // 记录结束上传的时间

            const actualTime = ((endTime - startTime) / 1000).toFixed(2); // 实际耗时，单位：秒
            document.getElementById("actualTime").innerText =
              "Actual Time: " + actualTime + "s";
          } else {
            reject();
          }
        };

        xhr.onerror = reject;

        const body = JSON.stringify({
          fileName: fileName,
          totalChunks: totalChunks
        });
        document.getElementById("mergeStatus").innerText = "开始合并...";
        xhr.send(body);
      });
    }

    function formatBytes(bytes, decimals = 2) {
      if (bytes === 0) return "0 Bytes";

      const k = 1024;
      const dm = decimals < 0 ? 0 : decimals;
      const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];

      const i = Math.floor(Math.log(bytes) / Math.log(k));

      return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
    }

    dropZone.addEventListener("dragover", (e) => {
      e.preventDefault();
      dropZone.style.backgroundColor = "#d9d9d9";
    });

    dropZone.addEventListener("dragleave", (e) => {
      e.preventDefault();
      dropZone.style.backgroundColor = "#fff";
    });

    dropZone.addEventListener("drop", (e) => {
      e.preventDefault();
      dropZone.style.backgroundColor = "#fff";
      const file = e.dataTransfer.files[0];

      // 创建新的 FileList 对象
      const fileList = new DataTransfer();
      fileList.items.add(file);

      // 将新的 FileList 对象赋值给文件输入框
      const fileInput = document.getElementById("file");
      fileInput.files = fileList.files;

      uploadFile();
    });
  </script>
</html>
